package net.simflow.swf.analyzer.network.data;

import java.net.URI;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;

import net.simflow.swf.analyzer.exec.data.ServiceTask;

public class Service implements ElementInNetwork {

	HashSet<Instance> instanceSet = new HashSet<Instance>();
	String nameInNetwork;
	URI wsdlURI;
	URI type;

	public Service(Instance instance) {
		wsdlURI = instance.getWSDLURI();
		type = instance.getType();
		instanceSet.add(instance);
	}

	public Service(Collection<? extends Instance> instances) {
		for (Instance ins : instances) {
			wsdlURI = ins.getWSDLURI();
			type = ins.getType();
			continue;
		}
		instanceSet.addAll(instances);
	}

	public String getNameInNetwork() {
		return nameInNetwork;
	}

	public void setNameInNetwork(String nameInNetwork) {
		this.nameInNetwork = nameInNetwork;
	}

	public boolean addInstance(Instance instance) {
		return (instance.getWSDLURI().equals(this.wsdlURI) && instance
				.getType().equals(this.type)) ? this.instanceSet.add(instance)
				: false;
	}

	public HashSet<Instance> getInstanceSet() {
		return instanceSet;
	}

	public URI getWsdlURI() {
		return wsdlURI;
	}

	public URI getType() {
		return type;
	}

	public String getShortInfo() {
		return getNameInNetwork();
	}

	public String getFullInfo() {
		StringBuffer str = new StringBuffer();

		str.append(nameInNetwork);
		str.append("(").append(this.getInstanceSet().size()).append(")");
		str.append("[");
		if (wsdlURI != null) {
			str.append("|").append(wsdlURI.toString()).append("|");
		}
		for (Instance ins : this.getInstanceSet()) {
			str.append(ins.getFullInfo());
			str.append(",");
		}
		str.deleteCharAt(str.length() - 1);
		str.append("]");

		return str.toString();
	}

	@Override
	public String toString() {
		return getFullInfo();
	}

	public static final int SCH_FIFO = 0;
	public static final int SCH_EDF = 1;
	public static final int SCH_EXX = 2;

	private int schedule;
	private LinkedList<ServiceTask> taskList = new LinkedList<ServiceTask>();
	private LinkedList<Integer> taskIdList = new LinkedList<Integer>();
	private int totalCount = 0;
	private int execCount = 0;
	private int unfinishedCount = 0;
	private int maxWaitTime;
	private int minWaitTime;
	private double avgWatiTime;
	private int time = 0;
	private int serviceAbility = 0;
	private double totalAbility = 0;
	private double totalStep = 0;
	private boolean isOverAbility = false;
	private int overAbilityTime = -1;

	public void initExec(int serviceAbility, int totalStep, int schedule) {
		this.schedule = schedule;
		this.taskList.clear();
		this.taskIdList.clear();
		this.totalCount = 0;
		this.execCount = 0;
		this.unfinishedCount = 0;
		this.time = 0;
		this.serviceAbility = serviceAbility;
		this.totalAbility = serviceAbility * totalStep;
		this.totalStep = totalStep;
		this.isOverAbility = false;
		this.maxWaitTime = 0;
		this.minWaitTime = Integer.MAX_VALUE;
		this.avgWatiTime = 0;
		this.overAbilityTime = -1;
	}

	public void appendTask(int taskId, Node node, int time, int flowStartTime,
			int flowDeadline) {
		int i;
		totalCount++;
		unfinishedCount++;

		switch (schedule) {
		case SCH_EXX:
			for (i = taskList.size() - 1; i >= 0; i--) {
				ServiceTask task = taskList.get(i);
				if (flowStartTime < task.getFlowStartTime()) {
					continue;
				} else {
					taskList.add(i + 1, new ServiceTask(taskId, node, time,
							flowStartTime, flowDeadline));
					taskIdList.add(i + 1, taskId);
					break;
				}
			}
			if (i < 0) {
				taskList.addFirst(new ServiceTask(taskId, node, time,
						flowStartTime, flowDeadline));
				taskIdList.addFirst(taskId);
			}
			break;
		case SCH_EDF:
			for (i = taskList.size() - 1; i >= 0; i--) {
				ServiceTask task = taskList.get(i);
				if (flowDeadline < task.getFlowDeadline()) {
					continue;
				} else {
					taskList.add(i + 1, new ServiceTask(taskId, node, time,
							flowStartTime, flowDeadline));
					taskIdList.add(i + 1, taskId);
					break;
				}
			}
			if (i < 0) {
				taskList.addFirst(new ServiceTask(taskId, node, time,
						flowStartTime, flowDeadline));
				taskIdList.addFirst(taskId);
			}
			break;
		case SCH_FIFO:
		default:
			taskList.add(new ServiceTask(taskId, node, time));
			taskIdList.add(taskId);
			break;
		}
		if (this.isOverAbility) {
			if (!taskList.isEmpty()) {
				taskList.removeLast();
				node.returnMishandledTask(taskIdList.removeLast(), time, this);
			}
			return;
		}
		if (taskList.size() > totalAbility) {
			this.isOverAbility = true;
			this.overAbilityTime = time;
			if (!taskList.isEmpty()) {
				taskList.removeLast();
				node.returnMishandledTask(taskIdList.removeLast(), time, this);
			}
			return;
		}
		taskList.getClass();
	}

	public void nextStep(int time) {
		this.totalAbility = serviceAbility * (totalStep - time);

		if (time <= this.time) {
			return;
		}
		this.time = time;
		if (taskList.isEmpty()) {
			return;
		}

		for (int i = 0; i < this.serviceAbility; i++) {
			ServiceTask task = null;
			try {
				if (taskList.isEmpty()) {
					return;
				}
				task = taskList.remove(0);
				taskIdList.remove(0);
			} catch (Exception e) {
				continue;
			}
			if (task == null) {
				return;
			}
			task.setEndTime(time);
			int waitTime = task.getEndTime() - task.getStartTime();
			if (waitTime > this.maxWaitTime) {
				this.maxWaitTime = waitTime;
			}
			if (waitTime < this.minWaitTime) {
				this.minWaitTime = waitTime;
			}
			this.avgWatiTime = (this.avgWatiTime * 1.0d * execCount + waitTime)
					/ (execCount + 1);
			execCount++;
			unfinishedCount--;
			task.getNode().returnTask(task.getId(), task.getFlowStartTime(),
					task.getFlowDeadline());
		}
	}

	public int getTotalCount() {
		return this.totalCount;
	}

	public int getExecCount() {
		return this.execCount;
	}

	public int getUnfinishCount() {
		return this.unfinishedCount;
	}

	public LinkedList<ServiceTask> getTaskList() {
		return this.taskList;
	}

	public LinkedList<Integer> getTaskIdList() {
		return this.taskIdList;
	}

	public int getMaxWaitTime() {
		return this.maxWaitTime;
	}

	public int getMinWaitTime() {
		return this.minWaitTime;
	}

	public double getAvgWatiTime() {
		return this.avgWatiTime;
	}

	public boolean isOverAbility() {
		return this.isOverAbility;
	}

	public int getOverAbilityTime() {
		return this.overAbilityTime;
	}
}
